﻿Imports System
Imports System.Collections
Imports System.Collections.Specialized
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Drawing
Imports System.Workflow.ComponentModel.Compiler
Imports System.Workflow.ComponentModel.Design

Namespace System.Workflow.ComponentModel
    <ActivityValidator(GetType(CompensateValidator)), SRCategory("Standard"), SRDescription("CompensateActivityDescription"), ToolboxItem(GetType(ActivityToolboxItem)), Designer(GetType(CompensateDesigner), GetType(IDesigner)), ToolboxBitmap(GetType(CompensateActivity), "Resources.Compensate.png")> _
    Public NotInheritable Class CompensateActivity
        Inherits Activity
        Implements IPropertyValueProvider, IActivityEventListener([Of] ActivityExecutionStatusChangedEventArgs)
        ' Methods
        Public Sub New()
        End Sub

        Public Sub New(ByVal name As String)
            MyBase.New(name)
        End Sub

        Private Function CompensateTargetActivity(ByVal context As ActivityExecutionContext) As ActivityExecutionStatus
            Dim activityByName As Activity = Nothing
            Dim activity2 As Activity = context.Activity
            Do
                activityByName = activity2.Parent.GetActivityByName(Me.TargetActivityName, True)
            Loop While (activityByName Is Nothing)
            If ((TypeOf activityByName Is ICompensatableActivity AndAlso (activityByName.ExecutionStatus = ActivityExecutionStatus.Closed)) AndAlso (activityByName.ExecutionResult = ActivityExecutionResult.Succeeded)) Then
                activityByName.RegisterForStatusChange(Activity.ClosedEvent, Me)
                context.CompensateActivity(activityByName)
                Return context.Activity.ExecutionStatus
            End If
            If (activityByName.ExecutionStatus = ActivityExecutionStatus.Initialized) Then
                Dim executionContextManager As ActivityExecutionContextManager = context.ExecutionContextManager
                Dim context2 As ActivityExecutionContext
                For Each context2 In executionContextManager.ExecutionContexts
                    If ((Not activityByName.GetActivityByName(context2.Activity.QualifiedName, True) Is Nothing) AndAlso (((context2.Activity.ExecutionStatus = ActivityExecutionStatus.Compensating) OrElse (context2.Activity.ExecutionStatus = ActivityExecutionStatus.Faulting)) OrElse (context2.Activity.ExecutionStatus = ActivityExecutionStatus.Canceling))) Then
                        Return context.Activity.ExecutionStatus
                    End If
                Next
                Dim i As Integer = (executionContextManager.CompletedExecutionContexts.Count - 1)
                Do While (i >= 0)
                    Dim contextInfo As ActivityExecutionContextInfo = executionContextManager.CompletedExecutionContexts.Item(i)
                    If (CByte((contextInfo.Flags And PersistFlags.NeedsCompensation)) <> 0) Then
                        Dim context3 As ActivityExecutionContext = executionContextManager.DiscardPersistedExecutionContext(contextInfo)
                        If TypeOf context3.Activity Is ICompensatableActivity Then
                            context3.Activity.RegisterForStatusChange(Activity.ClosedEvent, Me)
                            context3.CompensateActivity(context3.Activity)
                        End If
                        Return context.Activity.ExecutionStatus
                    End If
                    i -= 1
                Loop
            ElseIf CompensationUtils.TryCompensateLastCompletedChildActivity(context, activityByName, Me) Then
                Return context.Activity.ExecutionStatus
            End If
            Return ActivityExecutionStatus.Closed
        End Function

        Protected Friend Overrides Function Execute(ByVal executionContext As ActivityExecutionContext) As ActivityExecutionStatus
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            Return Me.CompensateTargetActivity(executionContext)
        End Function

        Friend Shared Function GetCompensatableTargets(ByVal compensate As CompensateActivity) As StringCollection
            Dim strings As New StringCollection
            Dim parent As CompositeActivity = compensate.Parent
            Do While (Not parent Is Nothing)
                If ((TypeOf parent Is CompensationHandlerActivity OrElse TypeOf parent Is FaultHandlersActivity) OrElse TypeOf parent Is CancellationHandlerActivity) Then
                    parent = parent.Parent
                    If (Not parent Is Nothing) Then
                        If Helpers.IsCustomActivity(parent) Then
                            strings.Add([TryCast](parent.UserData.Item(UserDataKeys.CustomActivityDefaultName),String))
                        Else
                            strings.Add(parent.Name)
                        End If
                        Dim activity2 As Activity
                        For Each activity2 In parent.EnabledActivities
                            If TypeOf activity2 Is ICompensatableActivity Then
                                strings.Add(activity2.Name)
                            End If
                        Next
                    End If
                    Return strings
                End If
                parent = parent.Parent
            Loop
            Return strings
        End Function

        Protected Friend Overrides Sub Initialize(ByVal provider As IServiceProvider)
            If (MyBase.Parent Is Nothing) Then
                Throw New InvalidOperationException(SR.GetString("Error_MustHaveParent"))
            End If
            MyBase.Initialize(provider)
        End Sub

        Private Function System.Workflow.ComponentModel.Design.IPropertyValueProvider.GetPropertyValues(ByVal context As ITypeDescriptorContext) As ICollection Implements IPropertyValueProvider.GetPropertyValues
            If (context Is Nothing) Then
                Throw New ArgumentNullException("context")
            End If
            Return CompensateActivity.GetCompensatableTargets(Me)
        End Function

        Private Sub System.Workflow.ComponentModel.IActivityEventListener<System.Workflow.ComponentModel.ActivityExecutionStatusChangedEventArgs>.OnEvent(ByVal sender As Object, ByVal e As ActivityExecutionStatusChangedEventArgs) Implements IActivityEventListener([Of] ActivityExecutionStatusChangedEventArgs).OnEvent
            If (sender Is Nothing) Then
                Throw New ArgumentNullException("sender")
            End If
            If (e Is Nothing) Then
                Throw New ArgumentNullException("e")
            End If
            Dim context As ActivityExecutionContext = [TryCast](sender,ActivityExecutionContext)
            If (context Is Nothing) Then
                Throw New ArgumentException("Error_SenderMustBeActivityExecutionContext", "sender")
            End If
            If (e.ExecutionStatus = ActivityExecutionStatus.Closed) Then
                e.Activity.UnregisterForStatusChange(Activity.ClosedEvent, Me)
                If (Me.CompensateTargetActivity(context) = ActivityExecutionStatus.Closed) Then
                    context.CloseActivity
                End If
            End If
        End Sub


        ' Properties
        <DefaultValue(""), MergableProperty(False), SRCategory("Activity"), SRDescription("CompensatableActivityDescr"), TypeConverter(GetType(PropertyValueProviderTypeConverter))> _
        Public Property TargetActivityName As String
            Get
                Return [TryCast](MyBase.GetValue(CompensateActivity.TargetActivityNameProperty),String)
            End Get
            Set(ByVal value As String)
                MyBase.SetValue(CompensateActivity.TargetActivityNameProperty, value)
            End Set
        End Property


        ' Fields
        Public Shared ReadOnly TargetActivityNameProperty As DependencyProperty = DependencyProperty.Register("TargetActivityName", GetType(String), GetType(CompensateActivity), New PropertyMetadata("", DependencyPropertyOptions.Metadata))
    End Class
End Namespace

